<?php

abstract class BaseQuery {
	public function __get($name) {
		return isset($this->$name) ? $this->$name : null;
	}

	protected function check($input) {
		$input = trim(utf8_filter($input));
    /*
		if (strlen($input) == 0) {
			throw new Exception("no value given");
		} else {
			return $input;
		}*/
    return $input;
	}

	abstract public function accept($obj);
}

class Query extends BaseQuery {
	protected $field, $value;

	public function __construct($field, $value) {
		$this->field = $this->check($field);
		$this->value = $this->check($value);
	}

	public function accept($obj) {
		$field = $this->field;
		if (is_null($obj->{$field})) {
			return false;
		} elseif ($obj->{$field} instanceof Data) {
			return $obj->{$field}->id == $this->value;
		} else {
			return $obj->{$field} == $this->value;
		}
	}
}

class PrefixQuery extends Query {
  public function accept($obj) {
    return strpos($obj->{$this->field}, $this->value) !== false;
  }
}

class SearchQuery extends Query {
  public function accept($obj) {
    return strpos($obj->{$this->field}, $this->value) !== false;
  }
}

class TrueQuery extends BaseQuery {
	public function accept($obj) {
		return true;
	}
}

class ExistQuery extends BaseQuery {
	protected $field;

	public function __construct($field) {
		$this->field = $this->check($field);
	}

	public function accept($obj) {
		return !is_null($obj->{$this->field});
	}
}

class DateRangeQuery extends RangeQuery {
  public function __construct($field, $lower = null, $upper = null) {
    parent::__construct($field,
      is_null($lower) ? $lower : date('Y-m-d H:i:s', $lower),
      is_null($upper) ? $upper : date('Y-m-d H:i:s', $upper)
    );
  }
}

class RangeQuery extends BaseQuery {
	protected $field, $lower, $upper;

	public function __construct($field, $lower = null, $upper = null) {
		$this->field = $this->check($field);
		$this->lower = $lower;
		$this->upper = $upper;
	}

	public function accept($obj) {
		return (  !is_null($obj->{$this->field})
			&& (is_null($this->upper) || $obj->{$this->field} <= $this->upper)
			&& (is_null($this->lower) || $obj->{$this->field} >= $this->lower)
		);
	}
}

class NotQuery extends BaseQuery {
	protected $query;

	public function __construct(BaseQuery $query) {
		$this->query = $query;
	}

	public function accept($obj) {
		return !$this->query->accept($obj);
	}
}

/*
 * 特殊类型的Query，基于字符串分析来拼装Query
 *
 * @todo 非法字符串的校验 + testcase by zhaojun.
 * */
class RawQuery extends BaseQuery {
	protected $query;

	public function __construct($queryString) {
		$this->query = QueryParser::parse($this->check($queryString));
	}

	public function accept($obj) {
		return $this->query->accept($obj);
	}
}

/*
 * 以下都是组合查询， 保证共同点：
 *  1. 都继承AndQuery
 *  2. 都有children
 * */
class AndQuery extends BaseQuery {
	protected $children = array();

	public function __construct() {
		foreach (func_get_args() as $q) {
			$this->add($q);
		}
	}

  public function children() {
    return $this->children;
  }

	public function add(BaseQuery $q) {
		if (get_class($q) == 'RawQuery') $q = $q->query;
		if (get_class($this) == get_class($q)) {
			$this->children = array_merge($this->children, $q->children);
		} else {
			$this->children[] = $q;
		}
	}

	public function accept($obj) {
		foreach ($this->children as $q) {
			if ($q->accept($obj) === false) {
				return false;
			}
		}
		return true;
	}
}

class OrQuery extends AndQuery {
	public function accept($obj) {
		foreach ($this->children as $q) {
			if ($q->accept($obj)) {
				return true;
			}
		}
		return false;
	}
}

class TokenQuery extends AndQuery {
  protected $field;
  public function __construct($field = null, Array $values = array()) {
    foreach ($values as $_val) {
      $this->add(new Query($field, $_val));
    }
  }

  public function add(BaseQuery $q) {
    if (get_class($q) != 'Query') {
      throw new Exception('It should be simple Query :' . var_export($q, true));
    }
    //InQuery需要保证所有children的Field是同一个值，否则和意义不符
    if (!$this->field) {
      $this->field = $q->field;
    } elseif ($this->field != $q->field) {
      throw new Exception("Different field given!");
    }
    $this->children[] = $q;
  }
}

class InQuery extends OrQuery {
	protected $field;
	public function __construct($field = null, Array $values = array()) {
		foreach ($values as $_val) {
			$this->add(new Query($field, $_val));
		}
	}

	public function add(BaseQuery $q) {
		if (get_class($q) != 'Query') {
			throw new Exception('It should be simple Query :' . var_export($q, true));
		}
		//InQuery需要保证所有children的Field是同一个值，否则和意义不符
		if (!$this->field) {
			$this->field = $q->field;
		} elseif ($this->field != $q->field) {
			throw new Exception("Different field given!");
		}
		$this->children[] = $q;
	}
}
